diff options
Diffstat (limited to 'app/[lng]/evcp/(evcp)/itb-create/page.tsx')
| -rw-r--r-- | app/[lng]/evcp/(evcp)/itb-create/page.tsx | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/app/[lng]/evcp/(evcp)/itb-create/page.tsx b/app/[lng]/evcp/(evcp)/itb-create/page.tsx new file mode 100644 index 00000000..54040e7f --- /dev/null +++ b/app/[lng]/evcp/(evcp)/itb-create/page.tsx @@ -0,0 +1,164 @@ +// app/[lng]/purchase-requests/page.tsx + +import * as React from "react"; +import { type SearchParams } from "@/types/table"; +import { getValidFilters } from "@/lib/data-table"; +import { Shell } from "@/components/shell"; +import { DataTableSkeleton } from "@/components/data-table/data-table-skeleton"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Plus, FileText, Clock, CheckCircle, XCircle, Send } from "lucide-react"; +import Link from "next/link"; +import { searchParamsPurchaseRequestCache } from "@/lib/itb/validations"; +import { getAllPurchaseRequests, getPurchaseRequestStats } from "@/lib/itb/service"; +import { PurchaseRequestsTable } from "@/lib/itb/table/purchase-requests-table"; + +interface PurchaseRequestsPageProps { + params: { + lng: string; + }; + searchParams: Promise<SearchParams>; +} + +export default async function PurchaseRequestsPage(props: PurchaseRequestsPageProps) { + const resolvedParams = await props.params; + const lng = resolvedParams.lng; + + const searchParams = await props.searchParams; + + // Parse search params + const search = searchParamsPurchaseRequestCache.parse(searchParams); + const validFilters = getValidFilters(search.filters); + + // Load data + const promises = Promise.all([ + getAllPurchaseRequests({ + ...search, + filters: validFilters, + }), + getPurchaseRequestStats(), + ]); + + return ( + <Shell className="gap-4"> + <div className="flex items-center justify-between"> + <div> + <h2 className="text-2xl font-bold tracking-tight"> + 구매 요청 관리 + </h2> + <p className="text-muted-foreground"> + 프로젝트별 자재 구매 요청을 생성하고 관리합니다. + </p> + </div> + </div> + + {/* 통계 카드 */} + <React.Suspense + fallback={ + <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-6"> + {[...Array(6)].map((_, i) => ( + <Card key={i}> + <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> + <CardTitle className="text-sm font-medium"> + <div className="h-4 w-20 bg-muted animate-pulse rounded" /> + </CardTitle> + </CardHeader> + <CardContent> + <div className="h-8 w-12 bg-muted animate-pulse rounded" /> + </CardContent> + </Card> + ))} + </div> + } + > + <PurchaseRequestStats promises={promises} /> + </React.Suspense> + + <React.Suspense + fallback={ + <DataTableSkeleton + columnCount={13} + searchableColumnCount={1} + filterableColumnCount={3} + cellWidths={[ + "8rem", // requestCode + "15rem", // requestTitle + "12rem", // projectCode + "15rem", // projectName + "10rem", // packageNo + "8rem", // status + "10rem", // engPicName + "10rem", // purchasePicName + "10rem", // estimatedBudget + "10rem", // requestedDeliveryDate + "8rem", // itemCount + "10rem", // createdAt + "8rem", // actions + ]} + shrinkZero + /> + } + > + <PurchaseRequestsTable promises={promises} /> + </React.Suspense> + </Shell> + ); +} + +// 통계 컴포넌트 +async function PurchaseRequestStats({ + promises +}: { + promises: Promise<[any, any]> +}) { + const [, stats] = await promises; + + const statCards = [ + { + title: "전체", + value: stats?.total || 0, + icon: FileText, + color: "text-blue-500", + }, + { + title: "작성중", + value: stats?.draft || 0, + icon: Clock, + color: "text-gray-500", + }, + + { + title: "RFQ 생성", + value: stats?.rfqCreated || 0, + icon: Send, + color: "text-red-500", + }, + ]; + + return ( + <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3"> + {statCards.map((card, index) => { + const Icon = card.icon; + return ( + <Card key={index}> + <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> + <CardTitle className="text-sm font-medium"> + {card.title} + </CardTitle> + <Icon className={`h-4 w-4 ${card.color}`} /> + </CardHeader> + <CardContent> + <div className="text-2xl font-bold">{card.value}</div> + </CardContent> + </Card> + ); + })} + </div> + ); +} + +// Metadata +export const metadata = { + title: "Purchase Request Management", + description: "Create and manage material purchase requests for projects", +};
\ No newline at end of file |
